package com.quectel.business.dms.application.access.accessor.im;

import cn.hutool.core.date.DateTime;
import com.quectel.business.common.util.sys.SysCodeUtils;
import com.quectel.business.dms.DubboServiceExportFactory;
import com.quectel.business.dms.application.access.AbstractApplicationAccessor;
import com.quectel.business.dms.application.access.accessor.im.model.PlaybackPart;
import com.quectel.business.dms.application.access.accessor.im.model.SearchMapReq;
import com.quectel.business.dms.application.access.accessor.im.model.SearchMapResp;
import com.quectel.business.dms.application.access.accessor.im.model.StreamResp;
import com.quectel.constant.OrderCodePrefixConstants;
import com.quectel.constant.core.grid.GridConstant;
import com.quectel.constant.sys.SysCodeKeyConstants;
import com.quectel.constant.sys.SysConfigKeyConstants;
import com.quectel.core.module.ai.dto.mongo.MongoAiEventRecordDto;
import com.quectel.core.module.ai.service.mongo.MongoAiEventRecordService;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.grid.dto.GridBindingDto;
import com.quectel.core.module.grid.dto.GridDto;
import com.quectel.core.module.grid.dto.GridEventRecordDto;
import com.quectel.core.module.grid.dto.GridEventRecordLogDto;
import com.quectel.core.module.grid.service.GridBindingService;
import com.quectel.core.module.grid.service.GridEventRecordService;
import com.quectel.core.module.grid.service.GridService;
import com.quectel.core.module.village.dto.VillageBuildingDto;
import com.quectel.core.module.village.dto.VillageDto;
import com.quectel.core.module.village.service.VillageBuildingService;
import com.quectel.core.module.village.service.VillageService;
import com.quectel.sdk.zlm.ZlmMediaKitClientBuilder;
import com.quectel.sdk.zlm.basemodel.ZlmAccessConfCls;
import com.quectel.sdk.zlm.basemodel.vo.StreamAddressAssembleResp;
import com.quectel.sdk.zlm.common.ZlmClient;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.kit.OrderUtils;
import com.quectel.util.spring.SpringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 智慧监控相关能力
 *
 * @author caolu
 * @email louis.cao@quectel.com
 */
public abstract class AbstractIntelligentMonitorAccessor
        extends AbstractApplicationAccessor {

    /**
     * 得到监控的直播流
     *
     * @param monitors 监控
     * @return 直播流地址 其中的设备如果没有流地址，则该位置的流对象用null代替 <br>
     * 返回的数组长度等于设备列表的长度
     */
    public abstract List<StreamResp> getStreamUrls(List<DeviceDto> monitors);

    /**
     * 得到监控的直播流
     *
     * @param monitor 监控
     * @return
     */
    public final StreamResp getStreamUrl(DeviceDto monitor) {
        return getStreamUrls(Collections.singletonList(monitor)).get(0);
    }


    /**
     * 如果方法getStreamUrls返回流是编码h265请重新实现 例如：
     *
     * @param monitors 监控
     * @return
     */
    public final List<String> getH264HlsStreamUrls(List<DeviceDto> monitors) {
        List<StreamResp> streams = getStreamUrls(monitors);
        if (CollectionUtils.isEmpty(streams)) {
            return Lists.newArrayList();
        }
        List<String> result = Lists.newArrayList();

        for (int i = 0; i < monitors.size(); i++) {
            StreamResp streamResp = streams.get(i);
            if (streamResp == null) {
                result.add(null);
            } else {
                if (StreamResp.StreamType.HLS264.equals(streamResp.getStreamType())
                        || StreamResp.StreamType.FLV264.equals(streamResp.getStreamType())
                ) {
                    result.add(streamResp.getStreamUrl());
                } else {
                    StreamAddressAssembleResp streamAddressAssembleResp = this.seekTransNodeAndAssembleAddr(monitors.get(i).getId(), streamResp.getStreamUrl());
                    result.add(streamAddressAssembleResp == null ? null : streamAddressAssembleResp.getHlsUrl());
                }
            }
        }
        return result;
    }


    /**
     * 一定返回http hls 264 永远不需要覆盖重新实现逻辑
     *
     * @param monitor 监控
     * @return
     */
    public final String getH264HlsStreamUrl(DeviceDto monitor) {
        return monitor == null ? null : getH264HlsStreamUrls(Arrays.asList(monitor)).get(0);
    }


    /**
     * 得到回放片段流
     *
     * @param monitor   监控
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围内具有的视频片段
     */
    public abstract List<PlaybackPart> getPlaybackParts(DeviceDto monitor, Date startTime, Date endTime);

    /**
     * 以图搜图
     *
     * @param req
     * @return
     */
    public SearchMapResp searchMap(SearchMapReq req) {
        return new SearchMapResp();
    }

    ;

    public void aiEventHandler(MongoAiEventRecordDto eventRecordDto) {
        DubboServiceExportFactory factory = SpringUtils.getBean(DubboServiceExportFactory.class);
        MongoAiEventRecordService aiEventRecordService = factory.getAiEventRecordService();

        final Date currentDate = new Date();
        eventRecordDto.setRecordTime(currentDate);
        Long save = aiEventRecordService.save(eventRecordDto);

        eventRecordDto.setId(save);
        saveGridEventRecordByAiEvent(eventRecordDto);

    }

    private void saveGridEventRecordByAiEvent(MongoAiEventRecordDto eventRecordDto) {
        DubboServiceExportFactory factory = SpringUtils.getBean(DubboServiceExportFactory.class);
        GridEventRecordService gridEventRecordService = factory.getGridEventRecordService();
        GridService gridService = factory.getGridService();
        VillageService villageService = factory.getVillageService();
        VillageBuildingService villageBuildingService = factory.getVillageBuildingService();

        //小区为空 则无法上报网格事件
        if (eventRecordDto.getVillageId() == null) {
            return;
        }
        String currentCode = null;
        Byte regionLevel = null;
        String address = null;
        VillageDto villageDto = villageService.selectCacheById(eventRecordDto.getVillageId());

        //根据楼栋查网格 如果网格存在 下发到网格 如果不存在则在小区级别处理
        if (eventRecordDto.getBuildingId() != null) {
            VillageBuildingDto villageBuildingDto = villageBuildingService.selectCacheById(eventRecordDto.getBuildingId());
            GridBindingService gridBindingService = factory.getGridBindingService();
            GridBindingDto gridBindingDto = gridBindingService.selectOne(GridConstant.SourceType.BUILDING.getType(), eventRecordDto.getBuildingId());
            GridDto gridDto = gridService.selectById(gridBindingDto.getGridId());
            if (gridDto != null) {
                currentCode = String.valueOf(gridDto.getId());
                regionLevel = GridConstant.RegionLevel.GRID_LEVEL.getLevel();
            } else {
                currentCode = String.valueOf(eventRecordDto.getVillageId());
                regionLevel = GridConstant.RegionLevel.VILLAGE_LEVEL.getLevel();
            }
            address = villageDto.getName() + villageBuildingDto.getName();
        } else {
            //没有绑定网格
            currentCode = String.valueOf(eventRecordDto.getVillageId());
            regionLevel = GridConstant.RegionLevel.VILLAGE_LEVEL.getLevel();
            address = villageDto.getName();
        }

        GridEventRecordDto gridEventRecordDto = new GridEventRecordDto();
        gridEventRecordDto.setTenantId(eventRecordDto.getTenantId());
        gridEventRecordDto.setVillageId(eventRecordDto.getVillageId());
        //创建级别为小区
        gridEventRecordDto.setCreatorLevel(GridConstant.RegionLevel.VILLAGE_LEVEL.getLevel());
        gridEventRecordDto.setCreatorUniqueFlag(String.valueOf(villageDto.getId()));
        gridEventRecordDto.setCurrentLevel(regionLevel);
        gridEventRecordDto.setCurrentCode(currentCode);
        gridEventRecordDto.setSourceType(GridConstant.GridEventRecordSourceType.INTELLIGENT_DISCOVERY.getType());
        gridEventRecordDto.setAiEventType(eventRecordDto.getEventType());
        gridEventRecordDto.setAiEventId(eventRecordDto.getId());
        gridEventRecordDto.setEventName(SysCodeUtils.easyToGetName(SysCodeKeyConstants.EVENT_TYPE, String.valueOf(eventRecordDto.getEventType())));
        gridEventRecordDto.setEventNo(OrderUtils.getOrderNo(OrderCodePrefixConstants.GRID_EVENT_RECORD_PREFIX));
        gridEventRecordDto.setEventAddr(address);
        gridEventRecordDto.setEventLevel(GridConstant.GridEventLevel.GENERAL.getLevel());
        gridEventRecordDto.setEventPicturesJson(eventRecordDto.getSnapImages());
        gridEventRecordDto.setStatus(GridConstant.GridEventRecordStatus.TO_BE_PROCESSED.getStatus());
        gridEventRecordDto.setEventTime(eventRecordDto.getRecordTime());
        gridEventRecordDto.setCreator("系统创建");

        //默认7天的逾期时间
        Calendar instance = Calendar.getInstance();
        instance.setTime(DateTime.now());
        instance.add(Calendar.DATE, Calendar.DAY_OF_WEEK);
        gridEventRecordDto.setDeadline(instance.getTime());
        gridEventRecordDto.setCreateTime(DateTime.now());

        List<GridEventRecordLogDto> logs = new ArrayList<>();
        GridEventRecordLogDto selfHandleLog = new GridEventRecordLogDto();
        selfHandleLog.setHandleStatus(GridConstant.GridEventRecordLogType.CREATE.getStatus());
        selfHandleLog.setHandleDesc("由系统自动创建");
        selfHandleLog.setCreateTime(DateTime.now());
        logs.add(selfHandleLog);

        gridEventRecordDto.setLogList(logs);
        gridEventRecordService.saveOrUpdateWithLog(gridEventRecordDto);

    }


    /**
     * 发起
     *
     * @param deviceId
     * @param originalUrl 原始地址
     * @return
     */
    private StreamAddressAssembleResp seekTransNodeAndAssembleAddr(Long deviceId, String originalUrl) {
        String configValueByKey = sysConfigService.getConfigValueByKey(SysConfigKeyConstants.ZLM_ACCESS_CONF);
        if (StringUtils.isBlank(configValueByKey)
                || CollectionUtils.isEmpty(JacksonUtils.parseArray(configValueByKey, ZlmAccessConfCls.class))) {
            return null;
        }

        List<ZlmAccessConfCls> zlmAccessConfCls = JacksonUtils
                .parseArray(configValueByKey, ZlmAccessConfCls.class);

        Set<Object> objects = ConcurrentHashMap.newKeySet();
        List<ZlmAccessConfCls> distinctClsList = zlmAccessConfCls.stream()
                .filter(o -> objects.add(o.getHost()))
                .collect(Collectors.toList());
        ZlmAccessConfCls targetNode = distinctClsList.get(Math.abs(deviceId.hashCode() % distinctClsList.size()));

        ZlmClient zlmClient = ZlmMediaKitClientBuilder.builder()
                .host(targetNode.getHost())
                .secret(targetNode.getSecret())
                .build();

        return zlmClient.streamAddressAssemble(deviceId.toString(), originalUrl);
    }
}
